import type { Agent, IncomingHttpHeaders } from 'node:http'
import { Buffer as NodeBuffer } from 'node:buffer'
import http from 'node:http'
import https from 'node:https'
import { URL } from 'node:url'
import { generateRandomFingerprint } from '@shared/utils/fingerprint'
import { getConfig } from '../config/index'

export interface HttpRequestOptions {
  method?: 'GET' | 'POST'
  url: string
  headers?: Record<string, string>
  body?: string | NodeBuffer
  referer?: string
  timeoutMs?: number
  // 可选：按请求覆盖代理（不影响会话默认代理）
  proxy?: { server: string, username?: string, password?: string }
}

export interface HttpResponse {
  status: number
  headers: IncomingHttpHeaders
  body: string
  url: string
}

/**
 * HTTP会话接口（用于多态）
 */
export interface IHttpSession {
  request: (opts: HttpRequestOptions) => Promise<HttpResponse>
  postForm: (url: string, form: Record<string, string>, referer?: string) => Promise<HttpResponse>
  getCurrentProxy: () => { server: string, username?: string, password?: string } | null
  banCurrentProxy: (reason?: string) => boolean
}

/**
 * 轻量 HTTP 会话，维护 Cookie 与最小请求间隔，支持代理
 */
export class HttpSession implements IHttpSession {
  private cookieJar: Map<string, string> = new Map()
  private lastRequestAt = 0
  private minIntervalMs: number
  private defaultHeaders: Record<string, string>
  private proxyAgent: Agent | null = null
  // 代理 Agent 缓存：避免频繁创建 HttpsProxyAgent
  private proxyAgentCache: Map<string, Agent> = new Map()
  private currentProxy: { server: string, username?: string, password?: string } | null = null

  constructor(userHeaders?: Record<string, string>, proxy?: { server: string, username?: string, password?: string }) {
    const cfg = getConfig()
    this.minIntervalMs = Math.max(0, Math.floor((cfg.network.global_min_interval_seconds || 0) * 1000))
    const fp = generateRandomFingerprint()
    this.defaultHeaders = {
      'User-Agent': (userHeaders && userHeaders['User-Agent']) ? userHeaders['User-Agent'] : fp.userAgent,
      'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
      'Accept-Language': fp.acceptLanguage,
      'Content-Type': 'application/x-www-form-urlencoded',
      ...(fp.secChUa ? { 'sec-ch-ua': fp.secChUa } : {}),
      ...(fp.secChUaMobile ? { 'sec-ch-ua-mobile': fp.secChUaMobile } : {}),
      ...(fp.secChUaPlatform ? { 'sec-ch-ua-platform': fp.secChUaPlatform } : {}),
      ...userHeaders,
    }

    // 设置代理（延迟初始化，在首次请求前完成）
    if (proxy && proxy.server) {
      this.currentProxy = proxy
      // 注意：setupProxyAgent 是异步的，但构造函数不能是异步的
      // 将在首次请求前确保代理已设置
      this.setupProxyAgent(proxy).catch(() => {})
    }
  }

  /**
   * 设置代理 Agent
   */
  private async setupProxyAgent(proxy: { server: string, username?: string, password?: string }): Promise<void> {
    try {
      const { HttpsProxyAgent } = await import('https-proxy-agent')

      // 构建代理 URL（包含认证信息）
      let proxyUrl = proxy.server
      if (proxy.username) {
        const cred = `${encodeURIComponent(proxy.username)}:${encodeURIComponent(proxy.password || '')}`
        // 确保URL格式正确：如果有认证信息，插入到协议和主机之间
        if (proxyUrl.includes('://')) {
          proxyUrl = proxyUrl.replace('://', `://${cred}@`)
        }
        else {
          // 如果没有协议，假设是http://
          proxyUrl = `http://${cred}@${proxyUrl}`
        }
      }

      console.log('[HttpSession] 设置代理Agent:', proxyUrl.replace(/:[^:@]*@/, ':****@')) // 隐藏密码

      // HttpsProxyAgent 可以处理 HTTP 和 HTTPS 代理
      // 添加选项以提高兼容性
      const agent = new HttpsProxyAgent(proxyUrl, {
        keepAlive: true, // 启用 keep-alive 复用连接，降低TLS握手频率
        keepAliveMsecs: 5000,
        maxSockets: 64,
        maxFreeSockets: 32,
      }) as Agent
      this.proxyAgent = agent
      this.proxyAgentCache.set(proxyUrl, agent)
    }
    catch (error) {
      console.warn('[HttpSession] 代理设置失败:', error)
      if (error instanceof Error) {
        console.warn('[HttpSession] 错误详情:', error.message, error.stack)
      }
      this.proxyAgent = null
    }
  }

  /**
   * 基于给定代理获取（或创建并缓存）代理 Agent
   */
  private async getAgentForProxy(proxy: { server: string, username?: string, password?: string }): Promise<Agent | null> {
    try {
      const { HttpsProxyAgent } = await import('https-proxy-agent')
      let proxyUrl = proxy.server
      if (proxy.username) {
        const cred = `${encodeURIComponent(proxy.username)}:${encodeURIComponent(proxy.password || '')}`
        if (proxyUrl.includes('://'))
          proxyUrl = proxyUrl.replace('://', `://${cred}@`)
        else
          proxyUrl = `http://${cred}@${proxyUrl}`
      }
      const cached = this.proxyAgentCache.get(proxyUrl)
      if (cached)
        return cached
      const agent = new HttpsProxyAgent(proxyUrl, {
        keepAlive: true,
        keepAliveMsecs: 5000,
        maxSockets: 64,
        maxFreeSockets: 32,
      }) as Agent
      this.proxyAgentCache.set(proxyUrl, agent)
      return agent
    }
    catch (e) {
      console.warn('[HttpSession] 获取代理Agent失败:', (e as any)?.message || String(e))
      return null
    }
  }

  /**
   * 获取当前使用的代理信息
   */
  getCurrentProxy(): { server: string, username?: string, password?: string } | null {
    return this.currentProxy
  }

  /**
   * 将当前代理加入黑名单
   */
  banCurrentProxy(reason?: string): boolean {
    if (!this.currentProxy)
      return false
    try {
      // 动态导入避免循环依赖
      // eslint-disable-next-line ts/no-require-imports
      const { updateConfig } = require('../config/index')
      const config = getConfig()
      if (!config?.proxy?.enabled)
        return false

      const ttlMin: number = Math.max(1, Number(config.proxy.blacklist_ttl_minutes || 60))
      const entry = { server: this.currentProxy.server, until: Date.now() + ttlMin * 60_000, reason }
      const blacklist = Array.isArray(config.proxy.temp_blacklist) ? config.proxy.temp_blacklist.slice() : []
      blacklist.push(entry)
      updateConfig({ proxy: { temp_blacklist: blacklist } as any }).catch(() => {})
      return true
    }
    catch {
      return false
    }
  }

  /**
   * 发送请求（自动附加 Cookie / 最小间隔 / 解析 Set-Cookie）
   */
  async request(opts: HttpRequestOptions): Promise<HttpResponse> {
    await this.ensureMinInterval()

    // 计算本次请求应使用的代理：优先 opts.proxy，其次会话默认代理
    const effectiveProxy = opts.proxy || this.currentProxy || null
    // 如果使用会话默认代理但 Agent 未初始化，确保初始化完成
    if (!opts.proxy && this.currentProxy && !this.proxyAgent)
      await this.setupProxyAgent(this.currentProxy)

    const url = new URL(opts.url)
    const isHttps = url.protocol === 'https:'
    const headers: Record<string, string> = { ...this.defaultHeaders, ...(opts.headers || {}) }

    // 合并 Cookie
    const cookieHeader = this.getCookieHeader()
    if (cookieHeader)
      headers.Cookie = cookieHeader

    // Referer（如提供）
    if (opts.referer)
      headers.Referer = opts.referer

    const reqOptions: https.RequestOptions = {
      protocol: url.protocol,
      hostname: url.hostname,
      port: url.port ? Number(url.port) : (isHttps ? 443 : 80),
      path: url.pathname + (url.search || ''),
      method: opts.method || 'GET',
      headers,
      timeout: opts.timeoutMs || Math.max(5000, getConfig().network.request_timeout_seconds * 1000),
      // Agent 在下方根据 effectiveProxy 决定
      agent: undefined,
    }

    // 根据 effectiveProxy 决定使用的 Agent
    if (effectiveProxy) {
      const agent = opts.proxy
        ? await this.getAgentForProxy(effectiveProxy)
        : (this.proxyAgent || null)
      if (agent)
        reqOptions.agent = agent
    }

    const body = opts.body

    const send = () => new Promise<HttpResponse>((resolve, reject) => {
      const lib = isHttps ? https : http
      const req = lib.request(reqOptions, (res) => {
        const chunks: NodeBuffer[] = []
        res.on('data', c => chunks.push(NodeBuffer.isBuffer(c) ? c : NodeBuffer.from(c as any)))
        res.on('end', () => {
          try {
            const setCookies = ([] as string[]).concat((res.headers['set-cookie'] as any) || [])
            if (setCookies && setCookies.length > 0)
              this.mergeSetCookies(setCookies)

            const bodyStr = NodeBuffer.concat(chunks).toString('utf8')
            const out: HttpResponse = {
              status: res.statusCode || 0,
              headers: res.headers,
              body: bodyStr,
              url: opts.url,
            }
            resolve(out)
          }
          catch (e) { reject(e) }
        })
      })
      req.on('error', (e) => {
        console.warn('[HttpSession] request error:', (e as any)?.message || String(e))
        reject(e)
      })
      req.on('timeout', () => {
        try {
          console.warn('[HttpSession] timeout for', opts.url)
          req.destroy(new Error('Request timeout'))
        }
        catch {}
      })
      if (body && (opts.method || 'GET') !== 'GET')
        req.write(body)
      req.end()
    })

    let resp: HttpResponse
    try {
      resp = await send()
    }
    catch (error) {
      const msg = (error as any)?.message ? String((error as any).message) : String(error)
      const alreadyRetried = (opts as any)._proxySchemeRetried === true
      if (this.currentProxy && msg.includes('CONNECT response') && !alreadyRetried) {
        // 尝试切换代理协议 http<->https 后重试一次
        const toggled = await this.tryAlternateProxyScheme()
        if (toggled) {
          ;(opts as any)._proxySchemeRetried = true
          return await this.request(opts)
        }
      }
      throw error
    }

    this.lastRequestAt = Date.now()
    return resp
  }

  /**
   * 简化的 POST 表单提交
   */
  async postForm(url: string, form: Record<string, string>, referer?: string): Promise<HttpResponse> {
    const body = new URLSearchParams(form).toString()
    const headers: Record<string, string> = { 'Content-Type': 'application/x-www-form-urlencoded' }

    // 如果提供了 referer，提取 origin（用于 POST 请求，某些服务器需要 Origin 头）
    if (referer) {
      try {
        const refererUrl = new URL(referer)
        headers.Origin = `${refererUrl.protocol}//${refererUrl.host}`
      }
      catch {
        // 忽略 URL 解析错误
      }
    }

    return await this.request({ method: 'POST', url, body, referer, headers })
  }

  private async ensureMinInterval(): Promise<void> {
    if (this.minIntervalMs <= 0)
      return
    const now = Date.now()
    const elapsed = now - this.lastRequestAt
    if (elapsed < this.minIntervalMs)
      await new Promise(r => setTimeout(r, this.minIntervalMs - elapsed))
  }

  private getCookieHeader(): string {
    if (this.cookieJar.size === 0)
      return ''
    return Array.from(this.cookieJar.entries()).map(([k, v]) => `${k}=${v}`).join('; ')
  }

  private mergeSetCookies(setCookies: string[]): void {
    for (const sc of setCookies) {
      // 只解析 name=value；忽略属性
      const semi = sc.indexOf(';')
      const pair = semi >= 0 ? sc.slice(0, semi) : sc
      const eq = pair.indexOf('=')
      if (eq > 0) {
        const name = pair.slice(0, eq).trim()
        const value = pair.slice(eq + 1).trim()
        if (name && value)
          this.cookieJar.set(name, value)
      }
    }
  }

  /**
   * 代理协议自动切换（在 CONNECT 失败时 http<->https 互换）
   */
  private async tryAlternateProxyScheme(): Promise<boolean> {
    if (!this.currentProxy)
      return false
    try {
      const src = this.currentProxy.server
      let next: string
      if (src.startsWith('http://'))
        next = src.replace(/^http:\/\//, 'https://')
      else if (src.startsWith('https://'))
        next = src.replace(/^https:\/\//, 'http://')
      else
        next = `https://${src}`

      const alt = { ...this.currentProxy, server: next }
      await this.setupProxyAgent(alt)
      this.currentProxy = alt
      return this.proxyAgent != null
    }
    catch {
      return false
    }
  }
}
